﻿using System;
using System.Reflection;
using System.Reflection.Emit;

namespace NForth3
{
    class Word
    {
        public Type ClassType { set; get; }
        public FieldInfo NameField { set; get; }
        public FieldInfo FlagsField { set; get; }
        public FieldInfo TypeField { set; get; }
        public FieldInfo PreviousField { set; get; }

        public Word(NForth nforth)
        {
            CreateType(nforth);
        }

        private void CreateType(NForth nforth)
        {
            ModuleBuilder module = nforth.Assembly.DefineDynamicModule("Word");
            TypeBuilder type = module.DefineType("Word", TypeAttributes.Public);

            // fields
            FieldBuilder name_field = type.DefineField("Name", typeof(string), FieldAttributes.Public);
            FieldBuilder flags_field = type.DefineField("Flags", typeof(int), FieldAttributes.Public);
            FieldBuilder type_field = type.DefineField("Type", typeof(Type), FieldAttributes.Public);
            FieldBuilder previous_field = type.DefineField("Previous", type, FieldAttributes.Public);

            // init method
            ConstructorBuilder constructor = type.DefineConstructor(
                MethodAttributes.Public, CallingConventions.Standard,
                new Type[] { typeof(string), typeof(int), typeof(Type), type});
            ILGenerator il = constructor.GetILGenerator();
            
            il.Emit(OpCodes.Ldarg_0);
            ConstructorInfo super_constructor = typeof(Object).GetConstructor(new Type[0]);
            il.Emit(OpCodes.Call, super_constructor);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Stfld, name_field);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_2);
            il.Emit(OpCodes.Stfld, flags_field);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_3);
            il.Emit(OpCodes.Stfld, type_field);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg, 4);
            il.Emit(OpCodes.Stfld, previous_field);
            il.Emit(OpCodes.Ret);

            type.CreateType();
            module.CreateGlobalFunctions();

            ClassType = module.GetType("Word");
            NameField = ClassType.GetField("Name");
            FlagsField = ClassType.GetField("Flags");
            TypeField = ClassType.GetField("Type");
            PreviousField = ClassType.GetField("Previous");
        }
    }
}
